home *** CD-ROM | disk | FTP | other *** search
- /*
- * 827.c
- *
- * By Jamie McCarthy, Sept 92. This is public domain.
- * If you have any questions or comments, you can reach
- * me at "k044477@kzoo.edu" on the Internet, or at
- * "j.mccarthy" on AppleLink.
- *
- * This code module isn't terribly exciting, but it does
- * provide a little more sample code, including doing
- * a callback from assembly language (whoopee!).
- *
- * I take responsibility for the messy code, but the
- * ugly symbol names with underscores in them were
- * Rich's idea. ;-)
- *
- */
-
-
-
- /******************************/
-
- #include <SetupA4.h>
- #include "ExternalInterface.h"
- #include "DialogUtilities.h"
-
- /******************************/
-
- /*
- * The DITL item numbers for the various controls.
- */
-
- enum {
- k_essetToB = 3,
- k_essetToSS,
-
- k_euroquotesToQuotes,
- k_euroquotesToInequalities,
-
- k_bulletToStar,
- k_bulletToO,
-
- k_convertSelection,
- k_convertDocument,
-
- k_changeInPlace,
- k_copyToClipboard,
-
- k_line
- } ;
-
- /******************************/
-
- /*
- * The preferences.
- */
- static struct
- {
- Boolean essetToSS;
- Boolean euroquotesToInequalities;
- Boolean bulletToO;
- Boolean convertDocument;
- Boolean copyToClipboard;
- } options8To7;
-
- /*
- * Was something selected when we started?
- */
- static Boolean nonEmptySelection;
-
- /*
- * These strings are only for chars with their hi bit set. Mask off
- * the hi bit, index into this array, and you have a _backwards_ C
- * string, max length 4, for what to replace. (They've got a max
- * length of 4 because they have to all be the same length, and you
- * can't have initialized strings in code segments, only char
- * constants. See the ThC5 User Manual, p. 496. And they're backwards
- * because the algorithm's fastest that way. :-) For length-4
- * strings, just put them in the char constant. For shorter strings,
- * stick a terminating null on the end and put whatever you like in
- * any of the remaining bytes. Note that if you write, say, 'a\0',
- * it will be interpreted as a 16-bit integer, and will be stored as
- * if you'd written '\0\0a\0', which is not what you want.
- *
- * Do not substitute a length-0 string for any char. The algorithm
- * doesn't support this. Sorry. Email me with a good reason for
- * mapping a character to nothing and I'll think about changing it.
- */
- static unsigned long replacement[128] = {
- /* 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 */
- /* 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F */
- 'eA\0x', 'A\0xx', 'C\0xx', 'E\0xx', 'N\0xx', 'eO\0x', 'eU\0x', 'a\0xx', // 0x80
- 'a\0xx', 'a\0xx', 'ea\0x', 'a\0xx', 'a\0xx', 'c\0xx', 'e\0xx', 'e\0xx', // 0x88
- 'e\0xx', 'e\0xx', 'i\0xx', 'i\0xx', 'i\0xx', 'i\0xx', 'n\0xx', 'o\0xx', // 0x90
- 'o\0xx', 'o\0xx', 'eo\0x', 'o\0xx', 'u\0xx', 'u\0xx', 'u\0xx', 'eu\0x', // 0x98
- '*\0xx', '*\0xx', 'c\0xx', '#\0xx', 'S\0xx', 'xxx\0', 'P\0xx', 'xxx\0', // 0xA0
- ')R(\0', ')c(\0', ')MT(', '\'\0xx',' \0xx', '><\0x', 'EA\0x', 'O\0xx', // 0xA8
- ')()(', '-/+\0', '=<\0x', '=>\0x', 'neY\0', 'u\0xx', 'd\0xx', 'muS\0', // 0xB0
- 'IP\0x', 'ip\0x', 'tnI\0', 'a\0xx', 'o\0xx', 'mhO\0', 'ea\0x', 'o\0xx', // 0xB8
- '?\0xx', '!\0xx', '-\0xx', '/\0xx', 'f\0xx', '=\0xx', 'D\0xx', 'xxx\0', // 0xC0
- 'xxx\0', '...\0', ' \0xx', 'A\0xx', 'A\0xx', 'O\0xx', 'EO\0x', 'eo\0x', // 0xC8
- '-\0xx', '--\0x', '"\0xx', '"\0xx', '\'\0xx','\'\0xx','/\0xx', 'o\0xx', // 0xD0
- 'ey\0x', 'eY\0x', '/\0xx', 'O\0xx', '<\0xx', '>\0xx', 'if\0x', 'lf\0x', // 0xD8
- '*\0xx', '.\0xx', ',\0xx', ',\0xx', '00/0', 'A\0xx', 'E\0xx', 'A\0xx', // 0xE0
- 'eE\0x', 'E\0xx', 'I\0xx', 'I\0xx', 'I\0xx', 'I\0xx', 'O\0xx', 'O\0xx', // 0xE8
- '*\0xx', 'O\0xx', 'U\0xx', 'U\0xx', 'U\0xx', 'i\0xx', '^\0xx', '~\0xx', // 0xF0
- '\'\0xx','\'\0xx','\'\0xx','*\0xx', ',\0xx', '"\0xx', ',\0xx', '\'\0xx' // 0xF8
- } ;
-
- /*
- * This array counts how much longer the replacement is than
- * what's it's replacing.
- */
- static char repExtraLength[256] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-
- 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1,
- 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 3, 0, 0, 1, 1, 0,
- 3, 2, 1, 1, 2, 0, 0, 2, 1, 1, 2, 0, 0, 2, 1, 0,
-
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 1,
- 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1,
- 0, 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- } ;
-
- /******************************/
-
- /*
- * How often should the progress thermometer be updated?
- */
-
- #define nPrgsBytes (16384L)
-
- /******************************/
-
- void maintain_buttons(DialogPtr d);
-
- void updateReplacementArray(void);
-
- long resizeTextHndl(ExternalCallbackBlock *callbacks,
- Handle text, long offset, long changeStart, long changeEnd,
- long *selStart, long *selEnd);
- void replaceText(ExternalCallbackBlock *callbacks,
- Handle text, long offset, long changeStart, long changeEnd,
- long textLen, long lengthIncrease);
-
- pascal void main(ExternalCallbackBlock *callbacks, WindowPtr w);
-
- /******************************/
-
-
-
- void maintain_buttons(DialogPtr d)
- {
- if (!nonEmptySelection) {
- XAbleDlgCtl(d, k_convertSelection, FALSE);
- options8To7.convertDocument = TRUE;
- }
- SetDlgCtl(d, k_essetToB, !options8To7.essetToSS);
- SetDlgCtl(d, k_essetToSS, options8To7.essetToSS);
- SetDlgCtl(d, k_bulletToStar, !options8To7.bulletToO);
- SetDlgCtl(d, k_bulletToO, options8To7.bulletToO);
- SetDlgCtl(d, k_euroquotesToQuotes, !options8To7.euroquotesToInequalities);
- SetDlgCtl(d, k_euroquotesToInequalities, options8To7.euroquotesToInequalities);
- SetDlgCtl(d, k_convertSelection, !options8To7.convertDocument);
- SetDlgCtl(d, k_convertDocument, options8To7.convertDocument);
- SetDlgCtl(d, k_changeInPlace, !options8To7.copyToClipboard);
- SetDlgCtl(d, k_copyToClipboard, options8To7.copyToClipboard);
- }
-
-
-
- void updateReplacementArray(void)
- {
- /*
- * Apparently, there's this nasty restriction about assigning
- * to global arrays in a code segment. I'm not sure why that is,
- * and I don't know if I'm gonna break something by doing this.
- *
- * But hey, let's fool the compiler. It's fun and easy! We'll
- * take the address where we'd like to stuff a value, cast its
- * type to a regular old (unsigned long *), and happily write
- * to that location.
- */
-
- if (options8To7.essetToSS) {
- *(unsigned long*)(&replacement[0x00A7 - 128]) = 'ss\0\0';
- *(char *)(&repExtraLength[0x00A7]) = 1;
- } else {
- *(unsigned long*)(&replacement[0x00A7 - 128]) = 'B\0\0\0';
- *(char *)(&repExtraLength[0x00A7]) = 0;
- }
-
- if (options8To7.euroquotesToInequalities) {
- *(unsigned long*)(&replacement[0x00C7 - 128]) = '<<\0\0';
- *(unsigned long*)(&replacement[0x00C8 - 128]) = '>>\0\0';
- *(char *)(&repExtraLength[0x00C7]) = 1;
- *(char *)(&repExtraLength[0x00C8]) = 1;
- } else {
- *(unsigned long*)(&replacement[0x00C7 - 128]) = '"\0\0\0';
- *(unsigned long*)(&replacement[0x00C8 - 128]) = '"\0\0\0';
- *(char *)(&repExtraLength[0x00C7]) = 0;
- *(char *)(&repExtraLength[0x00C8]) = 0;
- }
-
- if (options8To7.bulletToO) {
- *(unsigned long*)(&replacement[0x00A5 - 128]) = 'o\0\0\0';
- *(char *)(&repExtraLength[0x00A5]) = 0;
- } else {
- *(unsigned long*)(&replacement[0x00A5 - 128]) = '*\0\0\0';
- *(char *)(&repExtraLength[0x00A5]) = 0;
- }
- }
-
-
-
- /******************************/
-
-
-
- /*
- * In these routines, the serious ones where work actually gets done,
- * we want to call the DoProgress() callback. It's nice to give
- * feedback, just in case this is going to take a long time. (If
- * you haven't seen the progress-o-meter, try converting a lot of
- * text: less than about two megs, and the dialog doesn't even
- * show up on my IIci.)
- *
- * But we don't want to call DoProgress() after processing each
- * character; we'd spend lots more time doing that than actually
- * doing any work. But neither would it make sense to call the
- * callback after the loop was over, eh? So we strike a compromise:
- * we decide on a certain number of bytes, and only call
- * DoProgress() every time we reach that number of bytes. (It's
- * predefined as 16K.)
- *
- * One way of doing this would be to loop the pointer until it
- * reaches its destination, and meanwhile increment a counter
- * until it reached the value that means "call DoProgress()."
- * But that's a little wasteful; there's no sense keeping track
- * of two counters when one will do. So, both resizeTextHndl()
- * and replaceText() use a slightly more efficient approach.
- *
- * They set a "check pointer" to the initial pointer, and add in
- * that 16K. Then, they make sure it's not past the _end_ of
- * where they're supposed to be going. If it is, they set it to
- * the end. Then they do the loop, and the only test that's made
- * is against the check pointer. When the index pointer equals
- * the check pointer, if it's equal to the end pointer, we're
- * done! If not, call DoProgress(), add another 16K to the check
- * pointer, make sure it's not past the end pointer, and keep
- * looping. (The flow isn't _exactly_ the same in the assembler
- * and C versions, but they both do the same thing.)
- */
-
- long resizeTextHndl(ExternalCallbackBlock *callbacks,
- Handle text, long offset, long changeStart, long changeEnd,
- long *selStart, long *selEnd)
- /*
- * Walk through the text, figuring out how many chars we'll
- * need to extend the length by. Then resize the handle,
- * and return the number of chars that it was extended.
- */
- {
- long oldLength;
- register long extraLength;
- register char nExtraChars;
- long actualLength;
- register unsigned char *srcP;
- register unsigned char *checkP;
- unsigned char *selStartP, *selEndP, *changeEndP;
-
- oldLength = GetHandleSize(text);
-
- srcP = (unsigned char *) *text - offset + changeStart;
- selStartP = (unsigned char *) *text - offset + *selStart;
- selEndP = (unsigned char *) *text - offset + *selEnd;
- changeEndP = (unsigned char *) *text - offset + changeEnd;
- extraLength = 0;
- checkP = srcP + nPrgsBytes;
- if (checkP > selStartP) checkP = selStartP;
-
- /*
- * Walk through up to the start of the selection. While we're
- * walking here, selStart and selEnd get increased for each
- * extra char we'll be adding.
- */
- while (srcP < selStartP) {
- while (srcP < checkP) {
- nExtraChars = repExtraLength[*srcP++];
- extraLength += nExtraChars;
- if (nExtraChars > 0) {
- *selStart += nExtraChars;
- *selEnd += nExtraChars;
- }
- }
- /*
- * Assume this routine takes half the total time, and that
- * replaceText() takes the other half.
- */
- callbacks->DoProgress( (srcP - ((unsigned char*) *text - offset + changeStart)) / 2 );
- checkP += nPrgsBytes;
- if (checkP > selStartP) checkP = selStartP;
- }
-
- /*
- * Walk through, between the start and the end of the selection.
- * While walking here, only selEnd gets bumped up.
- */
- while (srcP < selEndP) {
- while (srcP < checkP) {
- nExtraChars = repExtraLength[*srcP++];
- extraLength += nExtraChars;
- if (nExtraChars > 0) {
- *selEnd += nExtraChars;
- }
- }
- callbacks->DoProgress( (srcP - ((unsigned char*) *text - offset + changeStart)) / 2 );
- checkP += nPrgsBytes;
- if (checkP > selEndP) checkP = selEndP;
- }
-
- /*
- * Walk after the selection. In this part of the document,
- * neither selStart nor selEnd need be pushed back.
- */
- while (srcP < changeEndP) {
- while (srcP < checkP) {
- extraLength += repExtraLength[*srcP++];
- }
- callbacks->DoProgress( (srcP - ((unsigned char*) *text - offset + changeStart)) / 2 );
- checkP += nPrgsBytes;
- if (checkP > changeEndP) checkP = changeEndP;
- }
-
- SetHandleSize(text, oldLength + extraLength);
-
- actualLength = GetHandleSize(text);
- if (actualLength != oldLength + extraLength) return -1;
- else return extraLength;
- }
-
-
-
- void replaceText(ExternalCallbackBlock *callbacks,
- Handle text, long offset, long changeStart, long changeEnd,
- long textLen, long lengthIncrease)
- /*
- * The main loop: run through and replace everything between
- * changeStart and changeEnd. Since the new text will be either
- * the same size or longer, we start at the _end_ and run to the
- * _beginning_.
- */
- {
- register unsigned char sc; // The source character: the 8-bit character
- // presently being converted.
- register long count;
- unsigned char *finalSP;
- unsigned char *initialSP;
-
-
- /*
- * First, take everything past changeEnd and bump it up where
- * it's supposed to go.
- */
-
- BlockMove(*text - offset + changeEnd,
- *text - offset + changeEnd + lengthIncrease,
- textLen - offset - changeEnd);
-
-
- /*
- * In code segments, ThC won't let you have more than two address
- * registers, even if you ask politely with sugar on top. So
- * I'm not going to bother asking, I'm just going to take them.
- */
- #define checkP a0 // When srcP gets here, check to see if we're done.
- #define scratchPtr a0 // A scratch pointer.
- #define replPtr a1 // The pointer into the replacement array.
- #define doProgPtr a1 // The pointer to the "DoProgress" callback.
- #define srcP a2 // The source pointer: this points to the 8-bit
- // characters that get converted.
- #define destP a3 // The destination pointer: this points to the
- // 7-bit characters that have been converted.
-
- asm {
- movem.l a2-a3,-(a7) // save the two address regs we're not supposed to have!
- move.l (text), scratchPtr // set up...
- move.l (scratchPtr), finalSP // ...
- move.l offset, count // ...
- sub.l count, finalSP // ...
- move.l finalSP, srcP // ...
- add.l changeEnd, srcP // ...srcP...
- move.l srcP, destP // ...
- move.l srcP, initialSP // ...initialSP...
- add.l lengthIncrease, destP // ...destP...
- move.l destP, checkP // ...checkP...
- move.l changeStart, count // ...
- add.l count, finalSP // ...and finalSP
- clr.l sc // clear the source char reg
- bra.s @updateCheckP // make sure we won't go too far
-
- @loop: move.b -(srcP), sc // load the source char reg
- bmi.s @char8 // if hi bit set, it's an 8-bit char
- // otherwise it's a normal 7-bit char
- @char7: move.b sc, -(destP) // just move it to the destination
- cmpa.l srcP, checkP // do we need to do a checkup?
- bne.s @loop // if not, continue
- bra.s @doCheckup // if so, do it
-
- @char8: lea replacement, replPtr // put the addr of the array into replPtr
- andi.b #0x7F, sc // mask off the hi bit of the 8-bit char
- add.b sc, sc // index 4*sc bytes into...
- adda.w sc, replPtr // ...the array, since each entry...
- adda.w sc, replPtr // ...is four bytes long
- moveq #3, count // put a maximum of four bytes at destP
- @movRep: move.b (replPtr)+, -(destP) // shove one byte
- tst.b (replPtr) // is it a null char?
- dbeq count, @movRep // if so, or if we've done four bytes, stop
-
- @test: cmpa.l srcP, checkP // do we need to do a checkup?
- bne.s @loop // if not, continue
- // if so, do it
-
- @doCheckup:
- cmpa.l finalSP, checkP // are we totally done?
- beq.s @done // yup, quit
- @showProgress: // nope, update progress thermometer
- movem.l d0-d1/a0-a1, -(a7) // save registers we'll need later
- move.l callbacks, doProgPtr // load the routine's address...
- movea.l OFFSET(ExternalCallbackBlock,DoProgress)(doProgPtr), doProgPtr
- move.l initialSP, count // Calculate how far we've gone, assuming
- add.l count, count // that resizeTextHndl() already took half
- sub.l srcP, count // the time. (initialSP-finalSP) is the
- sub.l finalSP, count // total distance, so ((2*initialSP) -
- lsr.l #1, count // finalSP - srcP)/2 is our progress.
- clr.w -(a7) // clear space for Boolean return
- move.l count, -(a7) // push "done" parameter onto stack
- jsr (doProgPtr) // do the callback!
- addq #2, a7 // ignore the result
- movem.l (a7)+, d0-d1/a0-a1 // restore those registers
-
- @updateCheckP:
- sub.l #nPrgsBytes, checkP // move checkP to the next checkup point
- cmpa.l finalSP, checkP // is this the last leg of our journey?
- bge.s @test // if not, continue
- movea.l finalSP, checkP // if so, make sure we don't go too far...
- bra.s @test // ...then continue
-
- @done: movem.l (a7)+,a2-a3 // restore those registers we "stole"
- }
- }
-
-
-
- /******************************/
-
-
-
- pascal void main(ExternalCallbackBlock *callbacks, WindowPtr w)
- {
- DialogPtr d;
- short item;
- GrafPtr save_port;
- long selStart, selEnd, firstChar;
-
- short act_len;
-
- OSErr err;
-
- RememberA0();
- SetUpA4();
-
- callbacks->GetPreference('827 ', sizeof(options8To7), &options8To7, &act_len);
-
- if (act_len < 0)
- {
- options8To7.essetToSS = FALSE;
- options8To7.euroquotesToInequalities = FALSE;
- options8To7.convertDocument = FALSE;
- options8To7.copyToClipboard = FALSE;
- };
-
- GetPort(&save_port);
- d = callbacks->CenterDialog(128);
- SetPort(d);
-
- SetupUserItem(d, k_line, callbacks->FrameDialogItem);
-
- callbacks->GetSelection(&selStart, &selEnd, &firstChar);
- nonEmptySelection = (selEnd > selStart);
-
- do
- {
- maintain_buttons(d);
-
- ModalDialog(callbacks->StandardFilter, &item);
-
- switch (item)
- {
- case k_essetToB: options8To7.essetToSS = FALSE; break;
- case k_essetToSS: options8To7.essetToSS = TRUE; break;
-
- case k_euroquotesToQuotes: options8To7.euroquotesToInequalities = FALSE; break;
- case k_euroquotesToInequalities: options8To7.euroquotesToInequalities = TRUE; break;
-
- case k_bulletToStar: options8To7.bulletToO = FALSE; break;
- case k_bulletToO: options8To7.bulletToO = TRUE; break;
-
- case k_convertSelection: options8To7.convertDocument = FALSE; break;
- case k_convertDocument: options8To7.convertDocument = TRUE; break;
-
- case k_changeInPlace: options8To7.copyToClipboard = FALSE; break;
- case k_copyToClipboard: options8To7.copyToClipboard = TRUE; break;
- }
- } while ((item != ok) && (item != cancel));
-
- DisposDialog(d);
- SetPort(save_port);
-
- if (item == ok)
- {
- Handle h, text;
- long textLen;
- long lengthIncrease;
- long changeStart, changeEnd;
- long offset;
- long extendLengthBy;
-
- callbacks->SetPreference('827 ', sizeof(options8To7), &options8To7, &act_len);
-
- h = callbacks->GetWindowContents(w);
- textLen = GetHandleSize(h);
-
- if (options8To7.convertDocument) {
- changeStart = 0; changeEnd = textLen;
- } else {
- changeStart = selStart; changeEnd = selEnd;
- }
-
- if (options8To7.copyToClipboard) {
- text = callbacks->Allocate(changeEnd-changeStart, FALSE);
- if (text == NULL) {
- callbacks->ReportOSError(memFullErr);
- goto done8To7;
- }
- BlockMove(*h + changeStart, *text, changeEnd-changeStart);
- offset = changeStart;
- } else {
- text = h;
- offset = 0;
- }
-
- updateReplacementArray();
-
-
- callbacks->StartProgress("\pConverting…", // string to display
- changeEnd-changeStart, // total distance to go
- FALSE); // can't cancel
-
- lengthIncrease = resizeTextHndl(callbacks,
- text, offset, changeStart, changeEnd,
- &selStart, &selEnd);
- if (lengthIncrease < 0) {
- callbacks->DoneProgress();
- callbacks->ReportOSError(memFullErr);
- goto done8To7;
- }
-
- /*
- * Here's where all the real work gets done!
- */
- replaceText(callbacks,
- text, offset, changeStart, changeEnd,
- textLen, lengthIncrease);
-
- callbacks->DoneProgress();
-
-
- if (options8To7.copyToClipboard) {
- HLock(text);
- ZeroScrap();
- PutScrap(changeEnd - changeStart + lengthIncrease, 'TEXT', *text);
- DisposHandle(text);
- } else {
- callbacks->ContentsChanged(w);
- callbacks->SetSelection(selStart, selEnd, firstChar);
- }
- }
-
- done8To7:
- RestoreA4();
- }
-
-